# coding: utf-8
import json
import time
from math import floor
from itertools import chain
from datetime import date

from sqlalchemy import and_, or_

from common.bid_notify.sql_statement import Statement
from common.constant import BID_USER_LIMIT, \
    DEMAND_WEB_CALL, TEL_X_AREA_MAP, SEVEN_MOOR_NUMBERS, \
    PARTIC_CACHE_KEY_PREFIX, BidQueueStatus, \
    USER_TRAINING_COUNT, MAINTAIN_USER_LIMIT, ZONE_STAT_EXCHANGE, COMMON_API_QUEUE
from common.models import model_factory, DemandCate, DemandStatus, ParticStatus
from common.producer import Producer
from common.utils import redis_client


def current_bid_users(session, demand_id, published_count):
    """
    create by ld_wu 2018-01-24
    当前项目的抢单人
    """

    demand = model_factory.demand.get_by_id(session, demand_id)

    # 抢单结束或者圆满完成和无效不再分配中标人
    if demand and demand.status in (100, 200, 410):
        return list()

    params = dict(
        demand_id=demand_id,
        published_count=published_count
    )

    winners = session.execute(
        Statement.five_min_winner_sql,
        params
    ).fetchall()

    return winners


def bid_limit(demand=None):
    """
    create by ld_wu 2018-01-24
    获取配置的中标人限制数量
    :return:
    """
    if not demand or demand.cate != DemandCate.MAINTAIN.value:
        limit = redis_client.get(BID_USER_LIMIT)
    else:
        # 维修中标人数单独维护
        limit = redis_client.get(MAINTAIN_USER_LIMIT)
    return int(limit) if limit else 1


def mark_web_call_flag(demand_id, published_count):
    """
    create by ld_wu 2018-01-26
    标识中标外呼任务启动
    :return:
    """
    redis_client.setnx(
        DEMAND_WEB_CALL.format(
            demand_id=demand_id, published_count=published_count
        ),
        1
    )


def clear_web_call_flag(demand_id, published_count):
    """
    create by ld_wu 2018-01-26
    清除外呼启动标识
    :return:
    """
    redis_client.delete(
        DEMAND_WEB_CALL.format(
            demand_id=demand_id, published_count=published_count
        )
    )


def usable_virtual_number(
        session, demand_id, area_id, tel_a, tel_b):
    """
    create by ld_wu 2018-01-26
    获取可用的虚拟号
    :return:
    """
    prov_id = str(area_id)[:2] + '0000'
    area_ids = TEL_X_AREA_MAP.get(prov_id)
    used_numbers = used_tel_x(session, demand_id, tel_a, tel_b)
    all_query = query = session.query(model_factory.number_pool_for_lt).filter(
        model_factory.number_pool_for_lt.reserved.__eq__(False),
        model_factory.number_pool_for_lt.deleted_at == 0,
        model_factory.number_pool_for_lt.enterprise_id == 0,
    ).order_by(
        model_factory.number_pool_for_lt.use_count.desc(),
        model_factory.number_pool_for_lt.updated_at.asc()
    )
    if used_numbers:
        all_query = query = query.filter(
            model_factory.number_pool_for_lt.tel.notin_(used_numbers)
        )
    if area_ids:
        query = query.filter(
            model_factory.number_pool_for_lt.area_id.in_(area_ids)
        )
    tel = query.first() or all_query.first()
    return tel


def used_tel_x(session, demand_id, tel_a, tel_b):
    """
    获取已被使用的虚拟号列表
    :param session:
    :param demand_id
    :param tel_a: 客户号码
    :param tel_b: 工程商号码
    :return:
    """
    # 已被使用及该项目绑定过的虚拟号
    used_tels = session.query(model_factory.virtual_numbers).filter(
        or_(
            and_(
                model_factory.virtual_numbers.valid == 1,
                and_(
                    or_(
                        model_factory.virtual_numbers.tel_a == tel_a,
                        model_factory.virtual_numbers.tel_b == tel_b
                    )
                )
            ),
            model_factory.virtual_numbers.demand_id == demand_id
        )

    ).with_entities(
        model_factory.virtual_numbers.tel_x
    ).all()
    return [each.tel_x for each in used_tels]


def bind_virtual_number(
        session, demand_id, area_id, tel_a, tel_b):
    tel = usable_virtual_number(
        session, demand_id, area_id, tel_a, tel_b
    )
    if tel:
        model_factory.virtual_numbers.create(
            session,
            tel_a=tel_a,
            tel_b=tel_b,
            demand_id=demand_id,
            tel_x=tel.tel
        )
        tel.use_count += 1
        return tel.tel
    return tel_a


def update_not_contact(session, partic, demand):
    """
    create_by ld_wu 2018-01-27
    超时未充值的需要惩罚
    """
    from common.bid_notify.message import MessageNotify

    if partic.is_robot:
        return

    # 代金券支付用户不扣
    if partic.voucher_id:
        return
    user_id = partic.supplier_id
    points = partic.offer
    # 免费竞价扣除1, 否则扣除30%
    punish_point = floor(float(points) * 0.3) or 1

    pack = model_factory.pack.get_by_args(session, uid=user_id)
    user = model_factory.user.get_by_id(session, user_id)
    current_point = pack.bid_point
    producer = Producer(ZONE_STAT_EXCHANGE)
    common_producer = Producer(COMMON_API_QUEUE)
    if partic.status == ParticStatus.NOT_CONTACT.value:
        leave_point = current_point + points - punish_point
        # 写入返还记录历史
        model_factory.bid_cost_record.create(
            session,
            user_id=user_id,
            cost_type=3,
            bid_point=points,
            left_bid_point=current_point + points,
            demand_id=partic.demand_id
        )
        producer.exchange_send(json.dumps({
            'return_bid_point': int(points),
            'return_bid_user_id': user_id,
            'war_zone_id': user.war_zone_id
        }))
        common_producer.send(
            json.dumps(
                dict(
                    backend='bid_stock_stat',
                    func='day_stat',
                    kwargs=dict(
                        stat_type='ret_bid_point',
                        stat_cnt=int(points),
                    )
                )
            )
        )
        if partic.demand_status != DemandStatus.FREE_ONGOING.value:
            MessageNotify.timeout_payback_notify(
                session, demand, user, int(points) - int(punish_point)
            )
    else:
        # 余额不足因未扣汇币，所以只扣除惩罚点
        leave_point = current_point - punish_point

    pack.bid_point = leave_point

    model_factory.bid_cost_record.create(
        session,
        user_id=user_id,
        cost_type=4,
        bid_point=punish_point,
        left_bid_point=pack.bid_point,
        demand_id=partic.demand_id
    )
    producer.exchange_send(json.dumps({
        'cost_bid_point': int(punish_point),
        'cost_bid_user_id': user_id,
        'war_zone_id': user.war_zone_id
    }))
    common_producer.send(
        json.dumps(
            dict(
                backend='bid_stock_stat',
                func='day_stat',
                kwargs=dict(
                    stat_type='cost_bid_point',
                    stat_cnt=int(punish_point),
                )
            )
        )
    )


def clear_cache(demand_id):
    """
    create by ld_wu 2018-01-25
    :return:
    """
    partic_info_keys = redis_client.keys(
        PARTIC_CACHE_KEY_PREFIX.format(demand_id=demand_id)
    )
    if partic_info_keys:
        redis_client.delete(*partic_info_keys)


def current_bid_count(session, demand_id, published_count, has_called=False):
    """
    create by ld_wu 2018-01-25
    当前中标队列
    :return:
    """
    status_list = (
        [BidQueueStatus.CALLED, BidQueueStatus.TRAIN]
        if has_called
        else
        [BidQueueStatus.WAITING, BidQueueStatus.CALLED, BidQueueStatus.TRAIN]
    )

    return session.query(model_factory.bid_user_queues).filter(
        model_factory.bid_user_queues.demand_id == demand_id,
        model_factory.bid_user_queues.published_count == published_count,
        model_factory.bid_user_queues.status.in_(
            status_list
        )
    ).count()


def increase_training_count(user_id):
    # 增加个人被赠送次数
    redis_client.zincrby(USER_TRAINING_COUNT.format(date.today()), user_id)


def overload_user_ids():
    return redis_client.zrangebyscore(
        USER_TRAINING_COUNT.format(date.today()), 5, 100
    )


def demand_training_count(session, demand_id, published_count):
    """
    获取项目赠单人数
    :return:
    """
    return session.query(model_factory.partic).filter(
        model_factory.partic.demand_id == demand_id,
        model_factory.partic.is_training.__eq__(True),
        model_factory.partic.published_count == published_count
    ).count()


def next_training_user(session, demand, published_count):
    """
    create by ld_wu 2018-02-08
    获取下一个赠单人
    :param session:
    :param demand:
    :param published_count:
    :return:
    """
    # 赠单次数少于5次
    # 赠送人被赠少于5次
    training_count = demand_training_count(session, demand.id, published_count)
    if training_count >= 5:
        return None
    overload_ids = overload_user_ids()

    cate_to_condition = {
        1: model_factory.user_tags.install.__eq__(True),
        2: model_factory.user_tags.repair.__eq__(True),
        3: model_factory.user_tags.rent.__eq__(True)
    }

    partic_users = session.query(model_factory.partic.supplier_id).filter(
        model_factory.partic.demand_id == demand.id,
        model_factory.partic.published_count == published_count
    ).all()
    partic_user_ids = [each.supplier_id for each in partic_users]

    user_query = session.query(model_factory.user).filter(
        model_factory.user.deleted_at == 0,
        model_factory.user.training_status == 1,
        model_factory.user.id.notin_(list(chain(overload_ids, partic_user_ids)))
    ).join(
        model_factory.user_to_area, model_factory.user.id == model_factory.user_to_area.user_id
    ).filter(
        model_factory.user_to_area.area_id == demand.area_id
    ).join(
        model_factory.user_tags, model_factory.user_tags.user_id == model_factory.user.id
    ).filter(
        cate_to_condition.get(demand.cate, False)
    )
    user_query = user_query.order_by(
        model_factory.user.wx_openid == '',
        model_factory.user.training_index.desc(),
        model_factory.user.created_at.desc()
    )
    return user_query.first()


def unbind_virtual_number(demand_id, user_id, demand_owner_mobile, session):
    """
    create by zhouhl 2017-11-08
    解绑axb虚拟号
    """

    # 解绑过期未联系的虚拟号
    # NOTE 需要考虑如何兼容多个竞价成功？
    user = model_factory.user.get_by_id(session, user_id)

    if user:
        # 查看是否已绑定虚拟号
        tel_x = model_factory.virtual_numbers.get_by_args(
            session,
            tel_a=demand_owner_mobile,
            tel_b=user.mobile,
            demand_id=demand_id,
            valid=True
        )

        if tel_x:
            # 解除虚拟号
            timestamp = int(time.time())
            tel_x.valid = False
            tel_x.updated_at = timestamp

            number = model_factory.number_pool_for_lt.get_by_args(
                session,
                tel=tel_x.tel_x
            )
            if number:
                # 更新虚拟号使用次数
                number.use_count -= 1
                number.updated_at = timestamp
